import java.rmi.RemoteException;

/**
 * @author Jamada
 */
public class NodeReferences {

    /**
     * This node's finger table.
     */
    private FingerTable fingerTable = null;

    /**
     * This node's successor.
     */
    private Node successor = null;

    /**
     * This node's predecessor.
     */
    private Node predecessor = null;

    /**
     * This node's local ID.
     */
    private int localID;
    
    /**
     * This node's host and port info.
     */
    private HostPort localHostPort = null;
    
    /**
     * All entries for this node.
     */
    private Entries entries;
    
    /**
     * Max number of nodes allowed in this chord.
     */
    private final int mValue;
    


    public NodeReferences(final int id, final HostPort hostPort, final Entries entries, final int mValue) {
        this.localID = id;
        this.localHostPort = hostPort;
        this.entries = entries;
        this.mValue = mValue;
        this.fingerTable = new FingerTable(this.localID, this);
    }
    
    
    public void setPredecessor(Node the_node){
    	predecessor = the_node;
    }
    
    public void setSuccessor(Node the_node){
    	successor = the_node;
    }
    
    public Node getPredecessor() {
        return this.predecessor;
    }
    
    public Node getSuccessor() {
        return this.successor;
    }
    
    /**
     * Determines the closest preceding node for the given ID based on finger
     * table, successor, and predecessor.
     * 
     * @param key ID to find closest preceding node for.
     * @return Reference on closest preceding node.
     */
    public Node getClosestPrecedingNode(final int key) {
    	//set to initial calling node due to the case where only one node
    	//is in the system. 
    	Node result = null;//predecessor.getNodeReferences().successor;
    	try {
    		result = predecessor.getNodeReferences().successor;
    		int pred_id = predecessor.getNodeID();
    		int succ_id = successor.getNodeID();
    		
			////this is or because of the boundary case with predecessor id is less than nodeID
			if(pred_id > this.localID){
				if(pred_id < key || key <= this.localID){
					return predecessor;
				}
			//when successor is less than predecessor	
			}else if(succ_id < this.localID){
				if(this.localID < key || key <= succ_id){
					return result;
				}
			}
    
    		if(pred_id < key && key <= this.localID){
    			return predecessor;
    		}
    		
    		if(this.localID < key && key <= succ_id){
    			return result;
    		}
			
			int start;
			int end;
			for(int i = this.mValue - 1; i >= 0; i--){

				start = (int) ((this.localID + Math.pow(2, i)) % Math.pow(2, this.mValue));
				end = (int) ((this.localID + Math.pow(2, i + 1)) % Math.pow(2, this.mValue));
				
				if(start < key || key <= end){
					result = this.fingerTable.getFinger(i).getNodeReferences().getClosestPrecedingNode(key);
					break;
				}
			}
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
        return result;
    }
    
    /**
     * Adds the given node reference to the finger table and successor, if
     * appropriate.
     * 
     * @param newReference Reference to be added to the local data structures.
     */
    public final synchronized void addReference(Node newReference) {
        //check for null pointer
    }
    
    /**
     * Adds the given node reference to the finger table and successor list AND
     * sets it as new predecessor reference, if appropriate. Appropriateness is
     * given if either the former predecessor reference was null
     * or the new reference's ID is located between the old predecessor ID and
     * this node's ID. Even if not appropriate as predecessor, the reference is
     * added to finger table and successor list.
     * 
     * @param potentialPredecessor Reference which should be this node's predecessor.
     */
    public void addReferenceAsPredecessor(Node potentialPredecessor) {
        //check for null pointer
    }
    
    /**
     * Removes the given node reference from the finger table and the successor
     * list. If the given reference is the current predecessor, the predecessor
     * reference will be null afterwards.
     * 
     * @param oldReference Reference to remove from ALL data structures.
     */
    public void removeReference(final Node oldReference) {
      //check for null pointer
    }
    
    /**
     * Closes the connection to the given reference, if it is not kept in any
     * data structure (ie. finger table, successor, predecessor) any more.
     * 
     * @param removedReference Node to which the connection shall be closed, if there exists no reference any more.
     */
    public void disconnectIfUnreferenced(Node removedReference) {
        
    }
    
    /**
     * Determines if the given reference is contained in any one data structure,
     * ie. finger table, successor list, or predecessor reference.
     * 
     * @param newReference Reference to look up.
     * @return true, if the reference is contained and false if not.
     */
    public final synchronized boolean containsReference(Node newReference) {
        boolean result = false;
        
        return result;
    }
    
    /**
     * 
     * @return
     */
    public int getMValue() {
        return this.mValue;
    }
    
    public FingerTable getFingerTable(){
    	return this.fingerTable;
    }
    
    
    
}
